#![allow(non_snake_case)]

use std::collections::HashMap;

use crate::{
    json_val_to_string,
    table::{context_menus::ContextMenusView, table_cell::TableBodyCell},
};
use icondata as i;
use leptos::prelude::*;
use leptos_icons::*;
use serde_json::Value;
use shq_common::prelude::{
    report::{get_special_row_props, SpecialRows},
    Align, ColumnCell, ColumnCells, ContextMenus,
};
use thaw_utils::{class_list, ArcOneCallback};
use web_sys::KeyboardEvent;

use super::TableState;
#[component]
pub fn ListTableServer(
    ///表格列头
    header_cells: Memo<ColumnCells>,
    ///是否多选
    multi_sel: bool,
    ///选择改变的回调
    #[prop(optional)]
    on_sel_change: Option<ArcOneCallback<Vec<usize>>>,
    ///是否显示边框
    #[prop(optional, into)]
    cell_border: bool,
) -> impl IntoView {
    let table_state =
        use_context::<ReadSignal<TableState>>().expect("ReadSignal<TableState>在上下文中不存在");
    let width = move || {
        header_cells.with(|hc| {
            let leafs = hc.get_leaf_cells();
            let mut width: u32 = 0;
            for l in leafs.iter().filter(|l| l.title.is_some()) {
                width += l
                    .list_props
                    .as_ref()
                    .map(|lp| lp.width as u32)
                    .unwrap_or(100);
            }
            if multi_sel {
                width + 60
            } else {
                width
            }
        })
    };
    Effect::new(move |_| {
        if let Some(on_sel_change) = on_sel_change.clone() {
            let sel_indexs = table_state.with_untracked(|ts| ts.selected_indexs());
            on_sel_change(sel_indexs.get());
        }
    });
    view! {
        <table class=class_list!("table bg-base-100 border-collapse border table-fixed",get_cell_border_class(cell_border)) style:width=move || format!("{}px", width())>
            <TableHeader header_cells table_state cell_border multi_sel/>
            <TableBody header_cells cell_border multi_sel table_state can_update=false/>
        </table>
    }.into_any()
}

#[component]
pub(super) fn TableHeader(
    header_cells: Memo<ColumnCells>,
    table_state: ReadSignal<TableState>,
    cell_border: bool,
    multi_sel: bool,
) -> impl IntoView {
    let header_view = move || {
        header_cells.with(|hc| {
        let trs: Vec<Vec<&ColumnCell>>=hc.get_column_cells();
        let sel_col=table_state.with_untracked(|ts|ts.selected_col());
        view! {
            <thead class="sticky top-0 z-20">
                {trs.iter().map(|tr|
                    view!{
                        <tr>
                            {
                                if multi_sel {
                                    Some(view! {<th class="sticky left-0 top-0 z-30 bg-teal-50 text-center" style:width=move || format!("{}px", 60)>
                                                    <input type="checkbox" class="checkbox" prop:checked=move||table_state.with_untracked(|ts| ts.is_selected_all())
                                                        on:click=move |e|{
                                                            e.stop_propagation();
                                                        }
                                                        on:input=move |e|{
                                                            e.stop_propagation();
                                                            let checked = event_target_checked(&e);
                                                            table_state.with_untracked(|ts|ts.set_selected_all(checked));
                                                        }
                                                    />
                                                </th>
                                    })
                                }else{
                                    None
                                }
                            }
                            {
                                let mut th_width=if multi_sel {60} else {0};
                                tr.iter().enumerate().map(|(_idx, cell)|{
                                    match cell {
                                        ColumnCell::Branch(branch)=>{
                                            let title=branch.title.clone();
                                            let col_span=branch.colspan;
                                            let row_span=branch.rowspan;
                                            Some(either_of::Either::Left(view!{
                                                <td colspan=col_span rowspan=row_span
                                                    class="z-10 text-center text-sm bg-teal-50 px-2"
                                                    class=("border",cell_border)
                                                    class=("border-bg-300",cell_border)>
                                                    {title}
                                                </td>
                                            }))
                                        }
                                        ColumnCell::Leaf(leaf)=>{
                                            let title=leaf.title.clone().unwrap_or("".to_string());
                                            let col_span=leaf.colspan;
                                            let row_span=leaf.rowspan;
                                            let width = leaf.list_props.as_ref().map(|lp| lp.width).unwrap_or(100);
                                            let is_leaf=header_cells.with_untracked(|hc|hc.is_leaf(leaf.name.as_str()));
                                            let col_name=leaf.name.clone();
                                            let is_fixed = leaf.list_props.as_ref().map(|lp| lp.fixed).unwrap_or(false);
                                            let is_filter_or_sorter = leaf.list_props.as_ref().map(|lp| {
                                                lp.filter||lp.sorter
                                            }).unwrap_or(false);
                                            let icon=if is_filter_or_sorter {
                                                table_state.with_untracked(|ts|ts.get_icon(leaf.name.as_str()))
                                            } else {
                                                None
                                            };
                                            let leaf_view=get_leaf_td(
                                                col_name,
                                                title,
                                                sel_col.write_only(),
                                                width,
                                                th_width,
                                                is_fixed,
                                                is_leaf,
                                                col_span,
                                                row_span,
                                                cell_border,
                                                is_filter_or_sorter,
                                                icon
                                            );
                                            if is_fixed {
                                                th_width+=leaf.list_props.as_ref().map(|lp| lp.width).unwrap_or(100);
                                            }
                                            Some(either_of::Either::Right(leaf_view))
                                        }
                                        ColumnCell::None=>{
                                            None
                                        }
                                    }
                                }).collect_view()
                            }
                        </tr>
                    }).collect_view()
                }
            </thead>
        }.into_any()
    })
    };
    view! {
        {header_view}
    }
}

pub fn get_leaf_td(
    col_name: String,
    title: String,
    sel_col: WriteSignal<Option<(String, bool)>>,
    width: u16,
    left_width: u16,
    is_fixed: bool,
    is_leaf: bool,
    col_span: u8,
    row_span: u8,
    cell_border: bool,
    is_filter_or_sorter: bool,
    icon: Option<i::Icon>,
) -> impl IntoView {
    let title2 = title.clone();
    let title_view = {
        let tw = title.len() * 5;
        let w2 = icon.is_some().then(|| width - 40).unwrap_or(width);
        if tw > w2 as usize {
            either_of::Either::Left(view! {
                <div class="flex-1 tooltip tooltip-bottom w-full" data-tip={title2}>
                    <div class="text-center text-sm overflow-hidden text-ellipsis">
                        {title}
                    </div>
                </div>
            })
        } else {
            either_of::Either::Right(view! {
                <div class="flex-1 text-center text-sm overflow-hidden text-ellipsis">
                    {title}
                </div>
            })
        }
    };
    let name_view = {
        let children_view = if is_leaf {
            if let Some(icon) = icon {
                Some(view! {<div class="flex-none"><Icon icon=icon/></div>})
            } else {
                None
            }
        } else {
            None
        };
        view! {
            <div class="flex cursor-pointer z-10" on:click=move|_|if is_filter_or_sorter {
                sel_col.update(|col|{
                    if let Some(c) = col.as_mut() {
                        *c = (col_name.to_string(), true);
                    } else {
                        *col = Some((col_name.to_string(), true));
                    }
                })
            }>
                {title_view}
                {children_view}
            </div>
        }
    };
    if is_fixed {
        view! {
            <th colspan=col_span rowspan=row_span
                class="sticky z-30 top-0 bg-teal-50 shadow-r-lg px-2"
                style:left=move || format!("{}px", left_width)
                style:width=move || format!("{}px", width)
                class=("border",cell_border)
                class=("border-bg-300",cell_border)>
                {name_view}
            </th>
        }
        .into_any()
    } else {
        view! {
            <td colspan=col_span rowspan=row_span
                class="z-10 bg-teal-50 inset-shadow-sm pl-2 pr-3"
                class=("border",cell_border)
                style:width=move || format!("{}px", width)
                class=("border-bg-300",cell_border)>
                {name_view}
            </td>
        }
        .into_any()
    }
}

#[component]
pub(super) fn TableBody(
    header_cells: Memo<ColumnCells>,
    #[prop(optional, into)] cell_border: bool,
    multi_sel: bool,
    #[prop(optional, into)] list_indexs: Signal<Vec<usize>>,
    table_state: ReadSignal<TableState>,
    can_update: bool,
) -> impl IntoView {
    let special_rows = use_context::<StoredValue<SpecialRows>>();
    let context_menus = use_context::<StoredValue<ContextMenus>>();
    let sel_indexs = table_state.with_untracked(|ts| ts.selected_indexs());
    let list_data = table_state.with_untracked(|ts| ts.list_data);
    let curr_cell = table_state.with_untracked(|ts| ts.curr_cell());
    let tr_click = move |idx: usize| {
        if multi_sel {
            sel_indexs.update(|s| {
                if s.iter().any(|idx2| idx2 == &idx) {
                    s.retain(|idx2| idx2 != &idx);
                } else {
                    s.push(idx);
                }
            });
        } else {
            let is_no_change =
                sel_indexs.with_untracked(|s| s.get(0).map(|v| v == &idx).unwrap_or_default());
            if is_no_change {
                return; // 如果当前选中行就是点击的行，则不需要更新
            }
            sel_indexs.set(vec![idx]);
            if let Some(context_menus) = context_menus.as_ref() {
                let menu_idx = context_menus.with_value(|cms| cms.get_row_context_menu());
                if let Some(menu_idx) = menu_idx {
                    table_state.with_untracked(|ts| ts.context_action_index().set(Some(menu_idx)));
                }
            }
        }
    };
    let on_keydown = move |e: KeyboardEvent| {
        if !can_update {
            return; // 如果不能更新，则不处理键盘事件
        }
        let key = e.key();
        if key != "Tab"
            && key != "ArrowRight"
            && key != "ArrowLeft"
            && key != "ArrowDown"
            && key != "ArrowUp"
        {
            return;
        }
        e.prevent_default();
        let (mut ridx, mut cidx) = curr_cell.get().unwrap_or((0, 0));
        header_cells.with_untracked(|hc| {
            let leafs = hc.get_leaf_cells();
            loop {
                if key == "Tab" || key == "ArrowRight" {
                    cidx += 1;
                    if cidx >= leafs.len() {
                        cidx = 0;
                        ridx += 1;
                    }
                } else if key == "ArrowLeft" {
                    if cidx == 0 {
                        cidx = leafs.len() - 1;
                        if ridx > 0 {
                            ridx -= 1;
                        }
                    } else {
                        cidx -= 1;
                    }
                } else if key == "ArrowDown" {
                    ridx += 1;
                } else if key == "ArrowUp" {
                    if ridx > 0 {
                        ridx -= 1;
                    }
                } else {
                    return; // 如果不是方向键，则不处理
                }
                if ridx >= list_data.with(|ld| ld.len()) {
                    return;
                }
                if let Some(leaf) = leafs.get(cidx) {
                    if leaf.list_props.as_ref().map_or(false, |lp| lp.edit) {
                        curr_cell.set(Some((ridx, cidx)));
                        return;
                    }
                }
                if ridx == 0 {
                    return;
                }
            }
        });
    };
    let has_cell_context_menus = context_menus
        .map(|cms| cms.with_value(|cms| cms.has_cell_context_menus()))
        .unwrap_or(false);
    let body_trs = move || {
        header_cells.with_untracked(|hc| {
            let leafs = hc.get_leaf_cells();
            let mut lis = list_indexs.get();
            list_data.with(|rows| {
                if lis.is_empty() {
                    for i in 0..rows.len() {
                        lis.push(i);
                    }
                }
                lis.into_iter()
                .map(|i| {
                    let row = rows.get(i).expect(format!("Row not found: {}", i).as_str());
                    let mut desc_vals: HashMap<usize, String> = HashMap::new();
                    let (row_data,srp) = match row {
                        Value::Array(arr) => {
                            let mut row_data:Vec<Value>=arr.clone();
                            let (srp, row_data2) = if let Some(sr)=special_rows {
                                sr.with_value(|sr| get_special_row_props(&hc, row_data, sr))
                            }else{
                                (None, row_data)
                            };
                            row_data = row_data2;
                            let no_idxs=hc.get_not_show_indexs();
                            let mut row:Vec<Value>=Vec::new();
                            for (i,val) in row_data.into_iter().enumerate() {
                                if no_idxs.contains(&i){
                                    continue;
                                }
                                row.push(val);
                            }
                            (row, srp)
                        }
                        Value::Object(mval) => {
                            let mut mval2:Vec<Value>=Vec::new();
                            for (i,leaf) in leafs.iter().enumerate() {
                                let cname=if let Some(dcn)=leaf.dict_col_name.as_ref() {
                                    dcn.as_str()
                                }else{
                                    leaf.name.as_str()
                                };
                                if let Some(desc)=mval.get(cname) {
                                    desc_vals.insert(i, json_val_to_string(desc));
                                    mval2.push(desc.clone());
                                }else{
                                    mval2.push(Value::Null);
                                }
                            }
                            (mval2,None)
                        }
                        _ =>(vec![],None),
                    };
                    let is_sel_row = move || {
                        if has_cell_context_menus {
                            return false;
                        }
                        sel_indexs.with(|idxs| {
                            let count = idxs.len();
                            if count > 1 {
                                idxs[count-1]==i
                            }else if count == 1 {
                                idxs[0]==i
                            }else{
                                false
                            }
                        })
                    };
                    view! {
                        <tr on:click=move |e|{
                            e.prevent_default();
                            e.stop_propagation();
                            if !multi_sel {
                                tr_click(i);
                            }
                        } on:contextmenu=move |e| {
                            e.prevent_default();
                            e.stop_propagation();
                            if !multi_sel {
                                tr_click(i);
                            }
                        } class=("hover",!has_cell_context_menus) class=("bg-base-300",move||is_sel_row())>
                            {
                                if multi_sel {
                                    Some(view!{
                                        <th class="sticky left-0 top-0 z-30 bg-teal-50 text-center">
                                            <input type="checkbox" class="checkbox" prop:checked=move||sel_indexs.with(|idxs|idxs.contains(&i))
                                                on:click=move |e|{
                                                    e.stop_propagation();
                                                }
                                                on:input=move |e|{
                                                    e.stop_propagation();
                                                    tr_click(i);
                                                }
                                            />
                                        </th>
                                    })
                                }else{
                                    None
                                }
                            }
                            {
                                let mut left_width=if multi_sel {60u16} else {0u16};
                                let mut row_view:Vec<_>=Vec::new();
                                for (j, value) in row_data.into_iter().enumerate() {
                                    let c=leafs[j];
                                    let dt=c.data_type;
                                    let lp=c.list_props.clone();
                                    let col_name=c.name.clone();
                                    let row_index = i;
                                    let col_index = j;
                                    let value_label=desc_vals.get(&j).cloned();
                                    let mut col_span:Option<u8>=None;
                                    let mut cell_val=value;
                                    let mut align:Option<Align> = None;
                                    if let Some(ref srp_data)=srp {
                                        let cidx=col_index as u8;
                                        if srp_data.is_merge {
                                            if cidx==srp_data.col_range.start {
                                                col_span=Some(srp_data.col_range.end-srp_data.col_range.start+1);
                                                cell_val=srp_data.show_value.clone();
                                                align = srp_data.align;
                                            }else if cidx>srp_data.col_range.start && cidx<=srp_data.col_range.end {
                                                continue
                                            }
                                        }else if srp_data.show_value!=Value::Null {
                                            if cidx>=srp_data.col_range.start && cidx<=srp_data.col_range.end {
                                                cell_val=srp_data.show_value.clone();
                                                align = srp_data.align;
                                            }
                                        }
                                    }
                                    let cell_view=view! {
                                        <TableBodyCell col_name dt lp=lp value=cell_val value_label cell_border row_index=row_index col_index=col_index col_span left_width can_update align is_select_cell=has_cell_context_menus context_menus table_state/>
                                    };
                                    left_width+=c.list_props.as_ref().map(|lp| lp.width).unwrap_or(100);
                                    row_view.push(cell_view);
                                }
                                row_view.collect_view()
                            }
                        </tr>
                    }
                })
                .collect_view()
            })
    })
    };
    view! {
        <tbody on:keydown=on_keydown>
            {body_trs}
            <Show when=move||context_menus.is_some()>
                <ContextMenusView context_menus=context_menus.unwrap() table_state/>
            </Show>
        </tbody>
    }
}
pub(super) fn get_cell_border_class(cell_border: bool) -> String {
    if cell_border {
        "border border-collapse border-bg-300".to_string()
    } else {
        "".to_string()
    }
}
